package kibahed.uni.mdp.representation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;


public class State {
	
	private double defaultReward = 1;
	
	private double walkOutsideReward = -1;
	
	private Map<Action,Double> rewards;
	
	private boolean absorbing;
	
	private Position position;
	
	private Map<Action,Map<Action,Double>> probabilities;
	
	private Map<Action,State> idealWay;
	
	private List<State> neighbours;
	
	private Map<State,Connection> neighbourConnections;
	
	private List<Connection> connections;
	
	private Random random;
	
	private List<Action> actions;
	
	private boolean isBad;
	
	public State(Position position) {
		this.position = position;
		connections = new ArrayList<Connection>();
		neighbourConnections = new HashMap<State,Connection>();
		random = new Random();
		idealWay = new HashMap<Action,State>();
		rewards = new HashMap<Action,Double>();
		actions = new ArrayList<Action>();
		neighbours = new ArrayList<State>();
	}
	
	public State(State state) {
		position = new Position(state.getPosition());
		connections = new ArrayList<Connection>(state.getConnections());
		neighbourConnections = new HashMap<State,Connection>(state.getNeighbourConnections());
		random = new Random();
		idealWay = new HashMap<Action,State>(state.getIdealWay());
		rewards = new HashMap<Action,Double>(state.getRewards());
		actions = new ArrayList<Action>(state.getActions());
		neighbours = new ArrayList<State>(state.getNeighbours());
		probabilities = new HashMap<Action,Map<Action,Double>>(state.getProbabilities());
		absorbing = state.isAbsorbing();
	}

	public Map<Action,Double> getProbailitiesForAction(Action a) {
		return probabilities.get(a);
	}
	
	public Map<State,Double> getProbabilitiesWithChosenAction(Action a) {
		Map<State,Double> probs = new HashMap<State,Double>();
	
		if(idealWay.containsKey(a)) {
				
			State aimState = idealWay.get(a);
			
			probs.put(aimState,neighbourConnections.get(aimState).getProbability());
			double pR = (1-neighbourConnections.get(aimState).getProbability())/(connections.size()-1);
			for(State s : neighbourConnections.keySet()) {
				
				
				if(!probs.containsKey(s)) {
					probs.put(s,pR);
				}
			}
		}
		return probs;
	}
	
	public State takeAction(Action a) {
		if(actions.contains(a)) {
			State nextState = null;
			Map<Action,Double> probs = probabilities.get(a);
			double r = random.nextDouble();
			double sum = 0;
			for(Action na : probs.keySet()) {
				sum += probs.get(na);
				if(sum > r) {
					nextState = idealWay.get(na);
					break;
				}
			}
			return nextState;
		} else {
			return this;
		}
	}
	
	public State changeToState(State aimState) {
		if(neighbourConnections.containsKey(aimState)) {
			Connection con = neighbourConnections.get(aimState);
			double p = con.getProbability();
			double r = random.nextDouble();
			if(r <= p) {
				return con.getEndState();
			} else {
				return connections.get(random.nextInt(connections.size()-1)).getEndState();
			}
		} else {
			return this;
		}
	}
	
	public void connectTo(State endState, Action action, double percentage) {
		Connection connection = new Connection(endState,action,percentage);
		connections.add(connection);
	}
	
	public Position getPosition() {
		return position;
	}

	public void setPosition(Position position) {
		this.position = position;
	}

	public List<Connection> getConnections() {
		return connections;
	}

	public void setConnections(List<Connection> connections) {
		this.connections = connections;
		
		for(Connection c : connections) {
			neighbours.add(c.getEndState());
			neighbourConnections.put(c.getEndState(), c);
			idealWay.put(c.getAction(), c.getEndState());
			actions.add(c.getAction());
		}
	}

	public boolean isAbsorbing() {
		return absorbing;
	}

	public void setAbsorbing(boolean absorbing) {
		this.absorbing = absorbing;
		if(absorbing) {
			connections.clear();
			connectTo(this, new Action(0,"stay"), 1);
		}
	}

	public double getDefaultReward() {
		return defaultReward;
	}

	public void setDefaultReward(double defaultReward) {
		this.defaultReward = defaultReward;
	}

	public Map<Action, Double> getRewards() {
		return rewards;
	}

	public void setRewards(Map<Action, Double> rewards) {
		this.rewards = rewards;
	}

	public double getWalkOutsideReward() {
		return walkOutsideReward;
	}

	public void setWalkOutsideReward(double walkOutsideReward) {
		this.walkOutsideReward = walkOutsideReward;
	}

	public void setRewardForAction(Action action, double reward) {
		rewards.put(action, reward);
	}
	
	public void setProbabilityForAction(Action action, double probability) {
		neighbourConnections.get(action).setProbability(probability);
	}
	
	public List<Action> getActions() {
		return actions;
	}

	public boolean isBad() {
		return isBad;
	}

	public void setBad(boolean isBad) {
		this.isBad = isBad;
	}

	public Map<Action,Map<Action,Double>> getProbabilities() {
		return probabilities;
	}

	public void setProbabilities(Map<Action,Map<Action,Double>> probabilities) {
		this.probabilities = probabilities;
	}
	
	public State getAimStateForAction(Action a) {
		return idealWay.get(a);
	}
	
	public String printPosition() {
		return "["+position.getRow()+","+position.getCollum()+"]";
	}


	public Map<Action, State> getIdealWay() {
		return idealWay;
	}


	public void setIdealWay(Map<Action, State> idealWay) {
		this.idealWay = idealWay;
	}


	public List<State> getNeighbours() {
		return neighbours;
	}


	public void setNeighbours(List<State> neighbours) {
		this.neighbours = neighbours;
	}


	public Map<State, Connection> getNeighbourConnections() {
		return neighbourConnections;
	}


	public void setNeighbourConnections(Map<State, Connection> neighbourConnections) {
		this.neighbourConnections = neighbourConnections;
	}


	public void setActions(List<Action> actions) {
		this.actions = actions;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((position == null) ? 0 : position.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		State other = (State) obj;
		if (position == null) {
			if (other.position != null)
				return false;
		} else if (!position.equals(other.position))
			return false;
		return true;
	}
}
 